<?php


    /*
    //通过自己的文件初使化一个配置
    self::init("config.php");

    //初始化shopnc的数据库配置
    self::init("../../data/config/config.ini.php",array(
        'db_host' => 'db.1.dbhost',
        'db_port' => 'db.1.dbport',
        'db_user' => 'db.1.dbuser',
        'db_pass' => 'db.1.dbpwd',
        'db_name' => 'db.1.dbname',
        'db_prefix' => 'tablepre'
    ),'config');

    //默认为Model
    echo Model()->table("cubi")->count();

    //设置另外一个粗鄙方法
    self::no_conflict("fkcubi");
    echo fkcubi()->table('cubi')->count();



    */


    class LightningStorm implements ArrayAccess {

        protected static $config = array();
        protected static $key_cache = null;
        protected static $cache_path = null;

        /**
         * 查询等不需要反馈的不用发起新连接
         *
         * @param  boolean $init_new_connection [description]
         * @return [type]                       [description]
         */


//    protected static function get_connection($init_new_connection = true){
//        if(self::$config == null){
//            die("you don't initialize database config!");
//        }
//        extract(self::$config);
//        if($init_new_connection || !self::$last_connection){
//            self::$last_connection = $connection = mysqli_connect($db_host . ':' . $db_port,$db_user,$db_pass,$db_name);
//            mysqli_query_ex("set names " . $db_charset,$connection);
//        }
//        return self::$last_connection;
//    }

//    protected static function init_default_value(&$arr){
//        //特殊处理
//        if(empty($arr['db_host'])){
//            $arr['db_host'] = 'localhost';
//        }
//        if(empty($arr['db_port'])){
//            $arr['db_port'] = '3306';
//        }
//        if(empty($arr['db_charset'])){
//            $arr['db_charset'] = 'utf8';
//        }
//    }

        protected static function get_value($var, $rule) {
            if (is_null($var)) {
                $var = '';
            }
            if (strpos($rule, '.') !== false) {
                $arr = explode('.', $rule);
                foreach ($arr as $v) {
                    if (isset($var[$v])) {
                        $var = $var[$v];
                    }
                }
            } else {
                if (isset($var[$rule])) {
                    return $var[$rule];
                }
            }
            return $var;
        }

        /**
         * 数据库配置初始化
         *
         * @param  [string]  $path       需要导入的文件
         * @param  [array]  $mapping    映射关系，db_name,db_pass,db_user为必需
         * @param  [boolean/string] 为true时，使用文件return出的结果，否则可以使用文件中声明的一个变量
         * @return [void]              [description]
         */
//    public static function init($path,$mapping = null,$has_result = true){
//        if($mapping == null){
//            self::$config = @require_once($path);
//        }
//        else{
//
//            $_fields = array('db_host','db_name','db_pass','db_prefix','db_user','db_port');
//            $arr = array();
//            if($has_result === true){
//                $_config = @include($path);
//            }
//            else{
//                @include($path);
//                $vars = get_defined_vars();
//                $_config = $vars[$has_result];
//            }
//
//            foreach($_fields as $val){
//                $arr[$val] = self::get_value($_config,$mapping[$val]);
//            }
//            self::init_default_value($arr);
//            self::$config = $arr;
//        }
//
//        self::init_default_value(self::$config);
//
//        /*
//            定义一个方法
//         */
////        if(!function_exists('Model')){
////            function Model($table_name = null){
////                return new LightningStorm($table_name);
////            }
////        }
//
//        /**
//         * 加载主键缓存
//         */
//        self::$cache_path = dirname(__FILE__) . '/';
//        self::$key_cache = @include(self::$cache_path . "__key_cache.php");
//
//    }

        /**
         * @param $func_name
         * @param $type
         * @param $config
         */
        public static function init($func_name, $type, $config) {
            self::$config[$func_name] = array(
                'type'   => $type,
                'config' => $config
            );
            $stmt = 'function ' . $func_name . ' ($table_name = null){ return new LightningStorm($table_name,"' . $func_name . '");}';

			if(!function_exists($func_name)){
				eval($stmt);
			}

//        exit;
//        if(function_exists('eval')){
//            echo 123;
//            exit;
//            eval($stmt);
//            return;
//        }

            /*
             * 暂时不考虑
             */
//        elseif(!file_exists(self::$cache_path . '__conflict.php')){
//            file_put_contents(self::$cache_path . '__conflict.php','<?php ' . $stmt);
//        }
//        @require_once(self::$cache_path . '__conflict.php');
        }


        /**
         * 实例方法定义
         */

        var $table_name;
        var $condition;
        var $order;
        var $limit;
        var $db_config;

        public function __construct($table_name, $func_name) {

            $this->db_config = self::$config[$func_name];

            if ($this->db_config['type'] == 'mysql') {
                if (!isset($this->db_config['config']['db_host'])) {
                    $this->db_config['config']['db_host'] = '127.0.0.1';
                }
                if (!isset($this->db_config['config']['db_port'])) {
                    $this->db_config['config']['db_port'] = '3306';
                }
                if (!isset($this->db_config['config']['db_charset'])) {
                    $this->db_config['config']['db_charset'] = 'utf8';
                }
            }

            if ($table_name) {
                $this->table($table_name);
            }

        }

        public function table($table_name, $absolute = false) {
            if ($absolute) {
                $this->table_name = $table_name;
            } else if (isset($this->db_config['config']['db_prefix'])) {
                $this->table_name = $this->db_config['config']['db_prefix'] . $table_name;
            } else {
                $this->table_name = $table_name;
            }
            return $this;
        }

        public function where($condition) {
            $this->condition = $condition;
            return $this;
        }

        /**
         * @param $ret PDOStatement
         * @return int
         */
        public function rowCount($ret) {
            $rows = $ret->fetch();
            return count($rows);
        }

        /**
         * 暂时不需要
         *
         * @return [type] [description]
         */
        public function select($condition = null) {
            if ($condition != null) {
                $this->where($condition);
            }
            $sql = "select * from " . $this->table_name . $this->get_where() . $this->get_order() . $this->get_limit();
            $db = $this->connect();
            $ret = $db->query($sql, PDO::FETCH_ASSOC);
            $ret = $ret->fetchAll();
            if (count($ret)) {
                return $ret;
            }
            return null;
        }

        public function insert($data, $is_replace = false) {
            $head = ($is_replace ? "replace into `{$this->table_name}`" : "insert into `{$this->table_name}`");
            $keys = array();
            $values = array();
            foreach ($data as $key => $val) {
                $keys[] = "`$key`";
                if (is_null($val)) {
                    $values[] = "null";
                } else {
                    $values[] = "'$val'";
                }
            }

            $sql = $head . '(' . implode(',', $keys) . ')values(' . implode(',', $values) . ')';
            $db = $this->connect();
            $ret = $db->exec($sql);
            if ($ret) {
                return $db->lastInsertId();
            } else {
                return false;
            }
        }

        public function delete($condition = null) {
            $this->anasylze_offset($condition);
            $head = "delete from `{$this->table_name}` ";
            $sql = $head . $this->get_where();
            $db = $this->connect();
            return $db->exec($sql);
        }

        public function order($order) {
            $this->order = $order;
            return $this;
        }

        public function limit($start, $len = null) {
            if (is_null($len)) {
                $this->limit = $start;
            } else {
                $this->limit = $start . ',' . $len;
            }
            return $this;
        }

        private function get_limit() {
            if ($this->limit) {
                return ' limit ' . $this->limit;
            }
            return '';
        }

        private function get_order() {
            if ($this->order) {
                return ' order by ' . $this->order;
            }
            return '';
        }

        private function get_pri_key() {
            //sqlite暂时只使用ID
            if ($this->db_config['type'] == 'sqlite') {
                return 'id';
            }
            if (self::$key_cache == null || !isset(self::$key_cache[$this->table_name])) {
                $ret = $this->query("desc " . $this->table_name);
                if (self::$key_cache == null) {
                    self::$key_cache = array();
                }
                if ($ret) {
                    foreach ($ret as $key => $val) {
                        if ($val['Key'] == 'PRI') {
                            self::$key_cache[$this->table_name] = $val['Field'];
                            break;
                        }
                    }
                    file_put_contents(self::$cache_path . '__key_cache.php', '<?php return ' . var_export(self::$key_cache, true) . '; ?>');
                }

            }
            return self::$key_cache[$this->table_name];
        }

        private function get_where() {
            if ($this->condition) {
                //string
                if (is_string($this->condition)) {
                    return ' where ' . $this->condition;
                }

                //array
                $head = ' where ';
                $buffer = array();
                foreach ($this->condition as $key => $val) {
                    if ($val === null && $val != 0) {
                        $buffer[] = "$key is null";
                        continue;
                    }
                    $buffer[] = "$key = '$val'";
                }
                return $head . implode(' and ', $buffer);
            }
            return '';
        }

        public function update($data) {
            $head = "update `{$this->table_name}` set ";
            $buffer = array();
            foreach ($data as $key => $val) {
                $buffer[] = "$key = '$val'";
            }
            $sql = $head . implode(',', $buffer) . $this->get_where();
            $db = $this->connect();
	        if($this->db_config['type'] == 'mysql'){
	        	return DB::execute($sql);
	        }
	        else{
		        return $db->exec($sql);
	        }
        }

        public function query($sql) {
            $db = $this->connect();
            $ret = $db->query($sql, PDO::FETCH_ASSOC);
//            if ($ret->rowCount()) {
		if($ret)
                return $ret->fetchAll();
//            }
            return false;
        }

        public function find($condition = null) {
            $this->anasylze_offset($condition);
            $sql = "select * from " . $this->table_name . ' ' . $this->get_where() . ' limit 1';
            $db = $this->connect();
            $ret = $db->query($sql, PDO::FETCH_ASSOC);
            $ret = $ret->fetch();
            if (count($ret)) {
                return $ret;
            }
            return null;
        }

        public function count($condition = null) {
            $this->anasylze_offset($condition);
            $sql = "select count(*) from " . $this->table_name . ' ' . $this->get_where();
            $db = $this->connect();
            $ret = $db->query($sql);
            $ret = $ret->fetch();
            if (count($ret)) {
                return $ret[0];
            }
            return 0;
        }

        /**
         * 清空设定的where limit order
         */
        public function cls() {
            $this->condition = $this->order = $this->limit = null;
            return $this;
        }

        private function connect() {
//        $db_name = $db_host = $db_user = $db_pass = null;
//
//        extract(self::$config);
            //self::$last_connection = $connection = mysqli_connect($db_host . ':' . $db_port,$db_user,$db_pass,$db_name);
            if ($this->db_config['type'] == 'mysql') {
                $db_name = $this->db_config['config']['db_name'];

                $db_host = $this->db_config['config']['db_host'] . ':' . $this->db_config['config']['db_port'];
                $db_user = $this->db_config['config']['db_user'];
                $db_pass = $this->db_config['config']['db_pass'];
                $dsn = "mysql:dbname={$db_name};host={$db_host}";
            } else {
                $dsn = 'sqlite:' . $this->db_config['config'];
                $db_user = '';
                $db_pass = '';
            }
//        if(self::$config[])
            try {
                $pdo = new PDO($dsn, $db_user, $db_pass, [PDO::ATTR_PERSISTENT => true]);
                $pdo->exec("set names utf8");
	            //$pdo->query("update ra_goods set goods_price = 90 where goods_id = 1183");
                return $pdo;
            } catch (PDOException $e) {
                echo 'Connection failed: ' . $e->getMessage();
            }
        }

        /**
         * 检查一个字符串是否是主键查询
         *
         * @param $str
         */
        private function check_if_uses_primiary_key($str) {
            return !preg_match("/[\s\n\t\r\=\<\>]/", trim($str));
        }


        /**
         * 以下为接口方法
         *
         * 该系列方法为上层方法的封装，不能和上层方法混合使用，所有方法在执行前均会执行一遍cls()
         */

        /**
         * Whether a offset exists
         *
         * @link  http://php.net/manual/en/arrayaccess.offsetexists.php
         * @param mixed $offset <p>
         *                      An offset to check for.
         *                      </p>
         * @return boolean true on success or false on failure.
         *                      </p>
         *                      <p>
         *                      The return value will be casted to boolean if non-boolean was returned.
         * @since 5.0.0
         */
        private function anasylze_offset($offset) {
            if ($offset == null) {
                return false;
            } else if (is_array($offset) || !$this->check_if_uses_primiary_key($offset)) {
                $this->where($offset);
            } else {
                $this->where($this->get_pri_key() . ' = ' . "'$offset'");
            }
            return true;
        }

        public function offsetExists($offset) {
            if ($offset == null) {
                return false;
            }
            // TODO: Implement offsetExists() method.

            return !!$this->count();
        }

        /**
         * Offset to retrieve
         *
         * @link  http://php.net/manual/en/arrayaccess.offsetget.php
         * @param mixed $offset <p>
         *                      The offset to retrieve.
         *                      </p>
         * @return mixed Can return all value types.
         * @since 5.0.0
         */
        public function offsetGet($offset) {
            if (!$this->anasylze_offset($offset)) {
                return null;
            }


            // TODO: Implement offsetGet() method.
        }

        /**
         * Offset to set
         *
         * @link  http://php.net/manual/en/arrayaccess.offsetset.php
         * @param mixed $offset <p>
         *                      The offset to assign the value to.
         *                      </p>
         * @param mixed $value  <p>
         *                      The value to set.
         *                      </p>
         * @return void
         * @since 5.0.0
         */
        public function offsetSet($offset, $value) {
            // TODO: Implement offsetSet() method.
        }

        /**
         * Offset to unset
         *
         * @link  http://php.net/manual/en/arrayaccess.offsetunset.php
         * @param mixed $offset <p>
         *                      The offset to unset.
         *                      </p>
         * @return void
         * @since 5.0.0
         */
        public function offsetUnset($offset) {
            if (!$this->anasylze_offset($offset)) {
                return false;
            }
            return $this->delete();
            // TODO: Implement offsetUnset() method.
        }
    }


?>